const SVG_NS = "http://www.w3.org/2000/svg";

class Dailychart {
  constructor(el, options) {
    // this.options = Dailychart.extend({}, this.defaultOptions, options);
    // if (!el) {
    //     throw new Error("Dailychart.js: el is not defined");
    // } else {
    //     this.element = el;
    // }
    // this.width = this.options.width || el.offsetWidth;
    // this.height = this.options.height || el.offsetHeight;
    // if (
    //     !el.getAttribute("data-dailychart-values") ||
    //     el.getAttribute("data-dailychart-values").length === 0
    // ) {
    //     return; // nothing to draw
    // } else {
    //     this.values = el
    //         .getAttribute("data-dailychart-values")
    //         .split(",")
    //         .map(Number);
    // }
    // if (this.values.length < 2) return; // at least two points needs to draw a line
    // if (
    //     !el.getAttribute("data-dailychart-length") ||
    //     el.getAttribute("data-dailychart-length").length === 0
    // ) {
    //     this.length = this.values.length;
    // } else {
    //     this.length = +el.getAttribute("data-dailychart-length");
    // }
    // if (
    //     !el.getAttribute("data-dailychart-close") ||
    //     el.getAttribute("data-dailychart-close").length === 0
    // ) {
    //     this.previous = this.values[0];
    // } else {
    //     this.previous = +el.getAttribute("data-dailychart-close");
    // }
    // this.normalize().translate().draw();
    this.numTest = /^[0-9]+.?[0-9]*/;
  }

  create(el, data, options) {
    if (typeof el === "string") {
      el = document.querySelectorAll(el);
      if (!el) return;
    }

    // if (ctx instanceof HTMLElement) {
    //     ctx = [ctx];
    // } else if (ctx instanceof NodeList || ctx instanceof HTMLCollection) {
    //     ctx = Array.from(ctx);
    // } else {
    //     throw new Error("Incorrect context was provided");
    // }

    if (!el) {
      throw new Error("TinyChart: el is not defined");
    } else {
      this.element = el;
    }

    this.values = data;
    if (this.values.length < 2) return;

    this.options = Dailychart.extend({}, this.defaultOptions, options);

    this.width = this.options.width || el.offsetWidth;
    this.height = this.options.height || el.offsetHeight;

    this.length = this.options.length || this.values.length;
    this.previous = this.options.close || this.values[0];

    this.normalize().translate().draw();
  }

  destroy(ctx) {
    if (typeof ctx === "string") {
      ctx = document.querySelectorAll(ctx);
      if (!ctx) return;
    }
    if (ctx instanceof HTMLElement) {
      ctx = [ctx];
    } else if (ctx instanceof NodeList || ctx instanceof HTMLCollection) {
      ctx = Array.from(ctx);
    } else {
      throw new Error("Incorrect context was provided");
    }

    ctx.forEach((el) => {
      el.remove() && el.parentNode.removeChild(el);
      el = null;
    });
  }

  // static create(ctx, options) {
  //     if (typeof ctx === "string") {
  //         ctx = document.querySelectorAll(ctx);
  //         if (!ctx) return;
  //     }

  //     if (ctx instanceof HTMLElement) {
  //         ctx = [ctx];
  //     } else if (ctx instanceof NodeList || ctx instanceof HTMLCollection) {
  //         ctx = Array.from(ctx);
  //     } else {
  //         throw new Error("Incorrect context was provided");
  //     }

  //     ctx.forEach((el) => new Dailychart(el, options));
  // }

  static extend(target, ...objects) {
    for (let object of objects) {
      for (let key in object) {
        let val = object[key];
        target[key] = val;
      }
    }
    return target;
  }

  normalize() {
    const min = Math.min.apply(null, this.values.concat([this.previous]));
    const max = Math.max.apply(null, this.values.concat([this.previous]));

    this.values = this.values.map((value) =>
      value ? (value - min) / (max - min) : value
    );
    this.previous = (this.previous - min) / (max - min);

    // console.log("normalize", this.values);
    return this;
  }

  translate() {
    const max = Math.max.apply(null, this.values.concat([this.previous]));
    const k = this.height / max;

    this.values = this.values.map((value) =>
      value ? this.height - value * k : value
    );
    this.previous = this.height - this.previous * k;

    // console.log("translate", this.values);
    return this;
  }

  id() {
    return Math.random().toString(36).substr(2, 9);
  }

  path() {
    const { lineWidth } = this.options;

    const inc = this.width / (this.length - 1);
    const d = [];

    let m = true;
    for (let i = 0; i < this.values.length; i++) {
      const v = this.values[i];
      //   d.push(i === 0 ? "M" : "L");
      //   d.push(i * inc);
      //   d.push(v + lineWidth);

      if (this.numTest.test(v)) {
        if (m) {
          d.push("M");
          m = false;
        } else {
          d.push("L");
        }

        d.push(i * inc);
        d.push(v + lineWidth);
      } else {
        m = true;
      }
    }

    return d.join(" ");
  }

  segments() {
    const { lineWidth } = this.options;

    const inc = this.width / (this.length - 1);
    let d = [],
      segments = [];

    let m = true;
    for (let i = 0; i < this.values.length; i++) {
      const v = this.values[i];
      //   d.push(i === 0 ? "M" : "L");
      //   d.push(i * inc);
      //   d.push(v + lineWidth);

      if (this.numTest.test(v)) {
        if (m) {
          d.push("M");
          m = false;
        } else {
          d.push("L");
        }

        d.push(i * inc);
        d.push(v + lineWidth);
      } else {
        m = true;
        segments.push(d);
        d = [];
      }

      if (i === this.values.length - 1) {
        segments.push(d);
      }
    }

    return segments;
  }

  draw() {
    const {
      lineWidth,
      colorPositive,
      colorNegative,
      fillPositive,
      fillNegative,
    } = this.options;

    const id = this.id();
    const idPositive = `dailychart-${id}-positive`;
    const idNegative = `dailychart-${id}-negative`;

    const d = this.path();
    // const dPositive = `${d} V ${this.height + lineWidth} H 0 Z`;
    // const dNegative = `${d} V 0 H 0 Z`;

    const svg = this.svgElement();
    const linePrevious = this.lineElement(this.previous);

    const pathPositive = this.pathElement(
      d,
      lineWidth,
      colorPositive,
      "",
      idPositive
    );
    const fillId = `grad-${id}`;
    const fillGradDefs = this.linearGradientDefs(fillId, fillPositive);
    // const areaPositive = this.pathElement(
    //   dPositive,
    //   0,
    //   "",
    //   fillPositive ? fillId : "",
    //   idPositive
    // );
    const clipPositive = this.clipElement(idPositive);
    const rectPositive = this.rectElement(0, 0, this.width, this.previous);

    const pathNegative = this.pathElement(
      d,
      lineWidth,
      colorNegative,
      "",
      idNegative
    );
    // const areaNegative = this.pathElement(
    //   dNegative,
    //   0,
    //   "",
    //   fillNegative,
    //   idNegative
    // );
    const clipNegative = this.clipElement(idNegative);
    const rectNegative = this.rectElement(
      0,
      this.previous,
      this.width,
      this.height - this.previous
    );

    clipPositive.appendChild(rectPositive);
    clipNegative.appendChild(rectNegative);

    svg.appendChild(fillGradDefs);
    svg.appendChild(clipPositive);
    svg.appendChild(clipNegative);

    svg.appendChild(linePrevious);

    // 绘制阴影面
    const segments = this.segments();
    for (let seg of segments) {
      const dPositive = `${seg.join(" ")} V ${this.height + lineWidth} H ${
        seg[1]
      } Z`;
      const areaPositive = this.pathElement(
        dPositive,
        0,
        "",
        fillPositive ? fillId : "",
        idPositive
      );
      fillPositive && svg.appendChild(areaPositive);

      const dNegative = `${d} V 0 H ${seg[1]} Z`;
      const areaNegative = this.pathElement(
        dNegative,
        0,
        "",
        fillNegative,
        idNegative
      );
      fillNegative && svg.appendChild(areaNegative);
    }
    // svg.appendChild(areaPositive);
    // svg.appendChild(areaNegative);
    svg.appendChild(pathPositive);
    svg.appendChild(pathNegative);

    this.element.appendChild(svg);
  }

  svgElement() {
    const svg = document.createElementNS(SVG_NS, "svg");

    svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", SVG_NS);
    svg.setAttribute("width", this.width);
    svg.setAttribute("height", this.height);
    // svg.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);

    return svg;
  }

  lineElement(y) {
    const line = document.createElementNS(SVG_NS, "line");

    line.setAttribute("x1", 0);
    line.setAttribute("y1", y);
    line.setAttribute("x2", this.width);
    line.setAttribute("y2", y);
    line.setAttribute("stroke", this.options.closeColor);
    line.setAttribute("stroke-width", this.options.closeWidth);

    line.style.shapeRendering = "crispEdges";

    return line;
  }

  pathElement(d, width, stroke, fill, clipId) {
    const path = document.createElementNS(SVG_NS, "path");

    path.setAttribute("d", d);
    // path.setAttribute("fill", fill === "" ? "none" : fill);
    path.setAttribute("fill", fill === "" ? "none" : `url(#${fill})`);
    path.setAttribute("stroke-width", width);
    path.setAttribute("stroke-linejoin", "round");
    path.setAttribute("stroke-linecap", "round");
    path.setAttribute("stroke", stroke === "" ? "none" : stroke);
    path.setAttribute("clip-path", `url(#${clipId})`);

    return path;
  }

  rectElement(x, y, w, h) {
    const rect = document.createElementNS(SVG_NS, "rect");

    rect.setAttribute("x", x);
    rect.setAttribute("y", y);
    rect.setAttribute("width", w);
    rect.setAttribute("height", h);

    return rect;
  }

  clipElement(id) {
    const clip = document.createElementNS(SVG_NS, "clipPath");

    clip.setAttribute("id", id);

    return clip;
  }

  linearGradientDefs(id, color) {
    // 创建渐变定义
    const defs = document.createElementNS(SVG_NS, "defs");
    const linearGradient = document.createElementNS(SVG_NS, "linearGradient");
    linearGradient.setAttribute("id", id);
    linearGradient.setAttribute("x1", "50%");
    linearGradient.setAttribute("y1", "0%");
    linearGradient.setAttribute("x2", "50%");
    linearGradient.setAttribute("y2", "100%");

    // 创建渐变中的颜色停靠点
    const stop1 = document.createElementNS(SVG_NS, "stop");
    stop1.setAttribute("offset", "0%");
    stop1.setAttribute("style", `stop-color:${color};stop-opacity:1`);
    const stop2 = document.createElementNS(SVG_NS, "stop");
    stop2.setAttribute("offset", "100%");
    stop2.setAttribute("style", `stop-color:${color};stop-opacity:0`);

    // 将停靠点添加到渐变中，然后将渐变添加到 defs 中，最后将 defs 添加到 SVG 中
    linearGradient.appendChild(stop1);
    linearGradient.appendChild(stop2);
    defs.appendChild(linearGradient);

    return defs;
  }
}

// close - the value splits the chart on two areas (positive, negative) with its own colors.
// length - the total number of data points. Used to scale the chart along the horizontal axis. If data-dailychart-values has fewer points the remaining space will be empty. On the other hand, if data-dailychart-length isn't defined the chart will fit the container.

// width - Chart width. If not set it equals to container's width.
// height - Chart height. If not set it equals to container's height.
// lineWidth - Line width of the graph (default: 1).
// colorPositive - Color of the positive part of the graph (default: '#33AE45').
// colorNegative - Color of the negative part of the graph (default: '#EB5757').
// fillPositive - Fill color of the positive area (default: '').
// fillNegative - Fill color of the negative area (default: '').
// closeWidth - Width of the close line (default: 1).
// closeColor - Color of the close line (default: '#e0e0e0').
Dailychart.prototype.defaultOptions = {
  width: undefined,
  height: undefined,
  lineWidth: 1,
  colorPositive: "#33AE45",
  colorNegative: "#EB5757",
  fillPositive: "",
  fillNegative: "",
  closeWidth: 1,
  closeColor: "#e0e0e0",
};

export default Dailychart;
